type Demo = 
    { feature: 'hello-world', runner: Function } |
    { feature: 'interface', runner: Function};

type ConciseDemo = {
    feature: 'hello-world' | 'interface',
    runner: Function,
};

const countLines = (text?: string[]): number => {
    let count = 0;
    for (const line of text || []) {
        if (line.length !== 0) {
            count += 1;
        }
    }
    return count;
};

const runOneHelloWorld = (lines?: string[]): void => {
    console.log(countLines(lines));
};

const runHelloWorld = () => {
    runOneHelloWorld(['one', 'two', 'three', 'four']);
    runOneHelloWorld();    
};

interface Person {
    name: string;
};

const sortByName = (a: Person[]): void => {
    a.sort((b, c) => b.name === c.name ? 0 : (b.name < c.name ? -1 : 1));
};

const getPersons = (n: number, maxValue: number): Person[] => {
    const result: Person[] = [];
    for (let i = 0; i < n; i++) {
        result.push({
            name: 'id-' + Math.floor(Math.random() * maxValue),
        });
    }
    return result;
};

const runInterface = () => {
    const persons = getPersons(10, 100);
    sortByName(persons);
    persons.forEach(person => console.log(person.name));
};

const runDemo = (demo: Demo): void => {
    demo.runner();
};

const runConciseDemo = (demo: ConciseDemo): void => {
    demo.runner();
};

// runDemo({ feature: 'hello-world', runner: runHelloWorld});
// runDemo({ feature: 'interface', runner: runInterface});
runConciseDemo({feature: 'hello-world', runner: runHelloWorld});

const assertNever = (obj: never) => {
    throw new Error('unexpected object: ' + JSON.stringify(obj));
};

const handleDemos = (demo: ConciseDemo): void => {
    switch (demo.feature) {
        case 'hello-world':
            console.log('the feature is hello world');
            return;
        case 'interface':
            console.log('the feature is interface');
            return;
    }
    assertNever(demo.feature);
}